#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# SPDX-License-Identifier: curl
#
###########################################################################
# 设置库的名称
set(LIB_NAME "libcurl")
# 设置 libcurl 库的基础名称，可通过 CMake 缓存进行配置
set(LIBCURL_OUTPUT_NAME "libcurl" CACHE STRING "Basename of the curl library")
# 为当前目录添加编译定义，表明正在构建 libcurl
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "BUILDING_LIBCURL")
# 为当前目录添加编译定义，包含 CURL 调试宏
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "${CURL_DEBUG_MACROS}")

# 根据 curl_config.h.cmake 模板生成 curl_config.h 文件
configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")

# 转换 Makefile.inc 文件为 CMake 可识别的文件
# 并获取 'CSOURCES' 和 'HHEADERS' 变量，分别代表源文件和头文件列表
curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
# 包含转换后的 Makefile.inc.cmake 文件
include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")

# 将生成的 curl_config.h 文件添加到头文件列表中
list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")

# 后续的构建配置

# 添加包含目录，以便编译器能够找到所需的头文件
include_directories(
  "${PROJECT_BINARY_DIR}/lib"  # 用于查找 curl_config.h
  "${PROJECT_SOURCE_DIR}/lib"  # 用于查找 curl_setup.h
)
# 若使用 c-ares 库，添加其包含目录
if(USE_ARES)
  include_directories(SYSTEM ${CARES_INCLUDE_DIRS})
endif()

# 若启用了 CURL 测试构建
if(CURL_BUILD_TESTING)
  # 创建一个仅用于单元测试的静态库 curlu
  add_library(
    curlu  # 库的名称
    STATIC  # 静态库类型
    EXCLUDE_FROM_ALL  # 从默认构建目标中排除
    ${HHEADERS} ${CSOURCES}  # 源文件和头文件列表
  )
  # 为 curlu 库添加编译定义，表明正在进行单元测试和使用静态库
  target_compile_definitions(curlu PUBLIC "UNITTESTS" "CURL_STATICLIB")
  # 为 curlu 库添加私有链接库
  target_link_libraries(curlu PRIVATE ${CURL_LIBS})
  # 构建 testdeps 目标时存在大量并行性
  # 覆盖 curlu 的批量构建大小为最大值以优化性能
  set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0)
endif()

# 若启用了 CURL 调试
if(ENABLE_CURLDEBUG)
  # 必须单独编译这些源文件，以避免 memdebug.h 重定义影响它们
  set_source_files_properties("memdebug.c" "curl_multibyte.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
endif()

## 库定义

# 若未定义导入库的后缀
if(NOT DEFINED IMPORT_LIB_SUFFIX)
  # 默认情况下，在 Windows 平台构建共享库且导入库后缀与静态库后缀相同时
  # 为导入库名称添加 "_imp" 后缀，以避免与生成的静态 "libcurl.lib" 冲突（通常在 MSVC 下）
  if(WIN32 AND BUILD_SHARED_LIBS AND
     CMAKE_IMPORT_LIBRARY_SUFFIX STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX)
    set(IMPORT_LIB_SUFFIX "_imp")
  else()
    set(IMPORT_LIB_SUFFIX "")
  endif()
endif()
# 若未定义静态库的后缀
if(NOT DEFINED STATIC_LIB_SUFFIX)
  set(STATIC_LIB_SUFFIX "")
endif()

# 检测导入库和静态库文件名冲突
if(WIN32 AND BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS AND
   "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" STREQUAL
   "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
  message(FATAL_ERROR "Library suffix is the same ('${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}') "
    "for the import and static '${LIBCURL_OUTPUT_NAME}' library. "
    "Set IMPORT_LIB_SUFFIX and/or STATIC_LIB_SUFFIX to different values, "
    "or disable building either the shared or static library to avoid the filename collision.")
endif()

# 是否对 libcurl 源文件进行单次编译，并重用这些对象来生成静态和共享目标
if(NOT DEFINED SHARE_LIB_OBJECT)
  # 默认在 Windows 平台启用，因为在这些平台上，PIC 是共享库和静态库的默认设置
  # 并且有一种方法可以告诉链接器应该导出哪些 libcurl 符号（而不是在编译时标记这些符号可导出）
  if(WIN32)
    set(SHARE_LIB_OBJECT ON)
  else()
    # 在其他平台上，将其作为一个默认禁用的选项
    set(SHARE_LIB_OBJECT OFF)
  endif()
endif()

# 若启用共享对象且 CMake 版本不低于 3.12
if(SHARE_LIB_OBJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
  # 设置对象库的名称
  set(LIB_OBJECT "libcurl_object")
  # 创建一个对象库，需要 CMake 3.12 及以上版本
  add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
  if(WIN32)
    # 始终定义 CURL_STATICLIB，以禁用导出的 libcurl 符号的 __declspec(dllexport)
    # 除非禁用符号隐藏或启用调试模式，此时我们从 libcurl DLL 导出所有符号，而不使用 libcurl.def
    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
  endif()
  # 为对象库添加私有链接库
  target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
  # 设置对象库的属性，使其具有位置无关代码
  set_target_properties(${LIB_OBJECT} PROPERTIES
    POSITION_INDEPENDENT_CODE ON)
  # 若 CURL 隐藏私有符号
  if(CURL_HIDES_PRIVATE_SYMBOLS)
    # 为对象库添加编译标志以隐藏符号
    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
    # 为对象库添加编译定义，表明隐藏符号
    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
  endif()
  # 若 CURL 支持链接时优化
  if(CURL_HAS_LTO)
    # 为对象库启用链接时优化
    set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
  endif()

  # 为对象库添加接口包含目录
  target_include_directories(${LIB_OBJECT} INTERFACE
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")

  # 设置库源为对象库的对象文件
  set(LIB_SOURCE $<TARGET_OBJECTS:${LIB_OBJECT}>)
else()
  # 否则，直接使用源文件和头文件列表
  set(LIB_SOURCE ${HHEADERS} ${CSOURCES})
endif()

# 我们希望在所有平台上都将其称为 libcurl
# 若构建静态库
if(BUILD_STATIC_LIBS)
  # 将静态库添加到导出列表中
  list(APPEND libcurl_export ${LIB_STATIC})
  # 创建静态库
  add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
  # 为静态库添加别名
  add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
  if(WIN32)
    # 为 Windows 平台的静态库添加编译定义，表明使用静态库
    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
  endif()
  # 为静态库添加私有链接库
  target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
  # 移除 "lib" 前缀，因为库已经命名为 "libcurl"
  set_target_properties(${LIB_STATIC} PROPERTIES
    PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
    SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
    INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB"
    INTERFACE_LINK_DIRECTORIES "${CURL_LIBDIRS}")
  # 若 CURL 隐藏私有符号
  if(CURL_HIDES_PRIVATE_SYMBOLS)
    # 为静态库添加编译标志以隐藏符号
    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
    # 为静态库添加编译定义，表明隐藏符号
    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
  endif()
  # 若 CURL 支持链接时优化
  if(CURL_HAS_LTO)
    # 为静态库启用链接时优化
    set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
  endif()

  # 为静态库添加接口包含目录
  target_include_directories(${LIB_STATIC} INTERFACE
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")
endif()

# 若构建共享库
if(BUILD_SHARED_LIBS)
  # 将共享库添加到导出列表中
  list(APPEND libcurl_export ${LIB_SHARED})
  # 创建共享库
  add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
  # 为共享库添加别名
  add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
  if(WIN32 OR CYGWIN)
    if(CYGWIN)
      # 对于 Cygwin，始终将 dllmain.c 作为单独单元编译，因为它包含 windows.h，不应包含在其他单元中
      set_source_files_properties("dllmain.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
    endif()
    # 将 dllmain.c 添加到共享库的源文件列表中
    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
  endif()
  if(WIN32)
    # 将 libcurl.rc 添加到共享库的源文件列表中
    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc")
    if(CURL_HIDES_PRIVATE_SYMBOLS)
      # 若 CURL 隐藏私有符号，将 libcurl.def 添加到共享库的源文件列表中
      set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def")
    endif()
  endif()
  # 为共享库添加私有链接库
  target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS})
  # 移除 "lib" 前缀，因为库已经命名为 "libcurl"
  set_target_properties(${LIB_SHARED} PROPERTIES
    PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
    IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
    POSITION_INDEPENDENT_CODE ON)
  # 若 CURL 隐藏私有符号
  if(CURL_HIDES_PRIVATE_SYMBOLS)
    # 为共享库添加编译标志以隐藏符号
    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
    # 为共享库添加编译定义，表明隐藏符号
    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
  endif()
  # 若 CURL 支持链接时优化
  if(CURL_HAS_LTO)
    # 为共享库启用链接时优化
    set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
  endif()

  # 为共享库添加接口包含目录
  target_include_directories(${LIB_SHARED} INTERFACE
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")

  # 根据不同平台设置 SOVERSION 默认值
  if(CMAKE_DLL_NAME_WITH_SOVERSION OR
    CYGWIN OR
    APPLE OR
    CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
    CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
    CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR
    CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR
    CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
    # FreeBSD 有 a.out 和 ELF 两种版本，但 a.out 支持到 v3.x，ELF 从 v3.x 开始。很难想象有人在这些古老系统上运行 CMake
    CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
    set(_soversion_default TRUE)
  else()
    set(_soversion_default FALSE)
  endif()

  # 定义一个 CMake 选项，用于控制是否启用 libcurl 的 SOVERSION，默认值根据平台而定
  option(CURL_LIBCURL_SOVERSION "Enable libcurl SOVERSION" ${_soversion_default})
  # 定义一个 CMake 选项，用于控制是否启用 libcurl 的版本化符号，默认禁用
  option(CURL_LIBCURL_VERSIONED_SYMBOLS "Enable libcurl versioned symbols" OFF)

  # 若启用 SOVERSION 或版本化符号
  if(CURL_LIBCURL_SOVERSION OR CURL_LIBCURL_VERSIONED_SYMBOLS)
    # 转换 Makefile.soname 文件为 CMake 可识别的文件
    # 并获取 'VERSIONCHANGE', 'VERSIONADD', 'VERSIONDEL', 'VERSIONINFO' 变量
    curl_transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
    # 包含转换后的 Makefile.soname.cmake 文件
    include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")

    # 计算 CMake 版本号
    math(EXPR _cmakesoname "${VERSIONCHANGE} - ${VERSIONDEL}")
    set(_cmakeversion "${_cmakesoname}.${VERSIONDEL}.${VERSIONADD}")
  endif()

  # 若启用 SOVERSION
  if(CURL_LIBCURL_SOVERSION)
    # 设置共享库的版本号和 SOVERSION
    set_target_properties(${LIB_SHARED} PROPERTIES
      VERSION "${_cmakeversion}" SOVERSION "${_cmakesoname}")
  endif()

  ## 版本化符号

  # 若启用版本化符号
  if(CURL_LIBCURL_VERSIONED_SYMBOLS)
    # 若未定义版本化符号的前缀
    if(NOT DEFINED CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX)
      # 默认使用 autotools 使用的前缀
      if(CURL_WITH_MULTI_SSL)
        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MULTISSL_")
      elseif(CURL_USE_OPENSSL)
        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_")
      elseif(CURL_USE_MBEDTLS)
        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_")
      elseif(CURL_USE_BEARSSL)
        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "BEARSSL_")
      elseif(CURL_USE_WOLFSSL)
        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_")
      elseif(CURL_USE_GNUTLS)
        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "GNUTLS_")
      elseif(CURL_USE_RUSTLS)
        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "RUSTLS_")
      endif()
    endif()
    # 生成链接器的版本脚本，用于版本化符号
    # 消耗的变量：
    #   CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX
    #   CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME
    set(CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME ${_cmakesoname})
    configure_file(
      "${CMAKE_CURRENT_SOURCE_DIR}/libcurl.vers.in"
      "${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers" @ONLY)
    # 包含 CMake 检查状态模块
    include(CMakePushCheckState)
    # 包含检查 C 源文件是否可编译的模块
    include(CheckCSourceCompiles)
    # 保存当前检查状态
    cmake_push_check_state()
    # 设置链接选项，使用生成的版本脚本
    set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers")
    # 检查带有版本脚本的 C 源文件是否可编译
    check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS)
    if(HAVE_VERSIONED_SYMBOLS)
      # 在 CMake 3.13 及以后版本中，被 LINK_OPTIONS 取代
      set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
    else()
      # 若不支持版本化符号，输出警告信息
      message(WARNING "Versioned symbols requested, but not supported by the toolchain.")
    endif()
    # 恢复之前保存的检查状态
    cmake_pop_check_state()
  endif()
endif()

# 为 LIB_SELECTED 目标添加别名
add_library(${LIB_NAME} ALIAS ${LIB_SELECTED})
add_library(${PROJECT_NAME}::${LIB_NAME} ALIAS ${LIB_SELECTED})

# 若启用导出目标
if(CURL_ENABLE_EXPORT_TARGET)
  # 若构建静态库，安装静态库目标
  if(BUILD_STATIC_LIBS)
    install(TARGETS ${LIB_STATIC}
      EXPORT ${TARGETS_EXPORT_NAME}
      ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    )
  endif()
  # 若构建共享库，安装共享库目标
  if(BUILD_SHARED_LIBS)
    install(TARGETS ${LIB_SHARED}
      EXPORT ${TARGETS_EXPORT_NAME}
      ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    )
  endif()

  # 导出目标到指定文件，并设置命名空间
  export(TARGETS ${libcurl_export}
    FILE "${PROJECT_BINARY_DIR}/libcurl-target.cmake"
    NAMESPACE ${PROJECT_NAME}::
  )
endif()
